<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>起跑发令计时系统</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3B82F6',
                        secondary: '#10B981',
                        accent: '#F59E0B',
                        danger: '#EF4444',
                        dark: '#1F2937',
                        light: '#F9FAFB'
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .timer-shadow {
                text-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
            }
            .btn-hover {
                @apply transition-all duration-300 hover:shadow-lg hover:-translate-y-0.5;
            }
            .input-focus {
                @apply focus:ring-2 focus:ring-primary/50 focus:border-primary;
            }
            .bg-pulse {
                animation: pulse-bg 0.05s ease-in-out;
            }
            @keyframes pulse-bg {
                0%, 100% { background-color: white; }
                50% { background-color: rgba(239, 68, 68, 0.9); }
            }
        }
    </style>
</head>
<body class="bg-gray-50 min-h-screen flex flex-col items-center justify-center p-4 font-sans">
    <div class="max-w-md w-full bg-white rounded-xl shadow-xl p-6 transform transition-all duration-500 hover:shadow-2xl">
        <h1 class="text-[clamp(1.5rem,3vw,2rem)] font-bold text-center text-dark mb-6 flex items-center justify-center">
            <i class="fa fa-bolt text-primary mr-2"></i>起跑发令计时系统
        </h1>
        
        <!-- 参数输入区域 
        <div class="mb-6 space-y-4">
            <div class="grid grid-cols-3 gap-3">
                <div class="relative">
                    <label for="varA" class="block text-sm font-medium text-gray-700 mb-1">最短时间(秒)</label>
                    <div class="relative">
                        <input type="number" id="varA" value="2" min="0.1" step="0.1" 
                            class="w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm input-focus outline-none">
                        <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
                            <span class="text-gray-500">秒</span>
                        </div>
                    </div>
                </div>
                
                <div class="relative">
                    <label for="varB" class="block text-sm font-medium text-gray-700 mb-1">最长时间(秒)</label>
                    <div class="relative">
                        <input type="number" id="varB" value="4" min="0.1" step="0.1" 
                            class="w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm input-focus outline-none">
                        <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
                            <span class="text-gray-500">秒</span>
                        </div>
                    </div>
                </div>
                
                <div class="relative">
                    <label for="varT" class="block text-sm font-medium text-gray-700 mb-1">停止时间(秒)</label>
                    <div class="relative">
                        <input type="number" id="varT" value="" min="1" step="1" 
                            class="w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm input-focus outline-none">
                        <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
                            <span class="text-gray-500">秒</span>
                        </div>
                    </div>
                </div>
            </div>-->
            <div class="grid grid-cols-2 md:grid-cols-4 gap-3">
                <div class="relative">
                    <label for="readyToSet" class="block text-sm font-medium text-gray-700 mb-1">各就位时间</label>
                    <div class="relative">
                        <input type="number" id="readyToSet" value="2" min="0.1" step="0.1" 
                            class="w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm input-focus outline-none">
                        <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
                            <span class="text-gray-500">秒</span>
                        </div>
                    </div>
                </div>
                
                <div class="relative">
                    <label for="setToGoA" class="block text-sm font-medium text-gray-700 mb-1">发令最短时间</label>
                    <div class="relative">
                        <input type="number" id="setToGoA" value="1" min="0.1" step="0.1" 
                            class="w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm input-focus outline-none">
                        <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
                            <span class="text-gray-500">秒</span>
                        </div>
                    </div>
                </div>
                
                <div class="relative">
                    <label for="setToGoB" class="block text-sm font-medium text-gray-700 mb-1">发令最长时间</label>
                    <div class="relative">
                        <input type="number" id="setToGoB" value="3" min="0.1" step="0.1" 
                            class="w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm input-focus outline-none">
                        <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
                            <span class="text-gray-500">秒</span>
                        </div>
                    </div>
                </div>
                
                <div class="relative">
                    <label for="countdownT" class="block text-sm font-medium text-gray-700 mb-1">报时时间</label>
                    <div class="relative">
                        <input type="number" id="countdownT" value="" min="1" step="1" 
                            class="w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm input-focus outline-none">
                        <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
                            <span class="text-gray-500">秒</span>
                        </div>
                    </div>
                </div>
            </div>
            
            <div class="text-sm text-gray-500 italic">
                <i class="fa fa-info-circle text-primary mr-1"></i>
                各就位时间：各就位-预备间的时长；发令最短和最长时间：控制预备-发令之间的随机间隔时间；报时时间：发令-计时结束时长，可以为空
            </div>
        
        <!-- 秒表显示区域 -->
        <div class="mb-6 flex flex-col items-center">
            <div id="timerDisplay" class="text-[clamp(2rem,6vw,3.5rem)] font-bold text-primary timer-shadow mb-2">
                00:00:000
            </div>
            <div class="flex text-xs text-gray-500 space-x-2">
                <span>分</span>
                <span class="mx-1">|</span>
                <span>秒</span>
                <span class="mx-1">|</span>
                <span>毫秒</span>
            </div>
        </div>
        
        <!-- 控制按钮区域 -->
        <div class="mb-6 flex justify-center space-x-4">
            <button id="startBtn" class="px-6 py-3 bg-secondary text-white rounded-lg font-medium flex items-center btn-hover">
                <i class="fa fa-play mr-2"></i>开始
            </button>
            <button id="stopBtn" class="px-6 py-3 bg-danger text-white rounded-lg font-medium flex items-center btn-hover" disabled>
                <i class="fa fa-stop mr-2"></i>停止
            </button>
        </div>
        
        <!-- 音效选择区域 -->
        <div class="mb-6">
            <label class="block text-sm font-medium text-gray-700 mb-2">音效选择</label>
            
            <div class="space-y-3">
                <!-- 蜂鸣音选项 -->
                <div class="bg-gray-50 p-3 rounded-lg border border-gray-200">
                    <div class="flex items-center">
                        <input type="radio" id="beepSound" name="soundType" value="beep" checked 
                            class="h-4 w-4 text-primary focus:ring-primary border-gray-300">
                        <label for="beepSound" class="ml-2 block text-sm font-medium text-gray-700">
                            蜂鸣音
                        </label>
                    </div>
                    <div class="mt-2 text-xs text-gray-600 ml-6">
                        使用Web Audio API生成的蜂鸣音作为提示音
                    </div>
                </div>
                
                <!-- 语音播报选项 -->
                <div class="bg-gray-50 p-3 rounded-lg border border-gray-200">
                    <div class="flex items-center">
                        <input type="radio" id="speechSound" name="soundType" value="speech" 
                            class="h-4 w-4 text-primary focus:ring-primary border-gray-300">
                        <label for="speechSound" class="ml-2 block text-sm font-medium text-gray-700">
                            语音播报
                        </label>
                    </div>
                    <div class="mt-2 text-xs text-gray-600 ml-6">
                        使用Web Speech API念出提示语
                    </div>
                </div>
                
                <!-- 语言选择 (仅在语音播报模式下可见) -->
                <div id="languageSelector" class="bg-gray-50 p-3 rounded-lg border border-gray-200 hidden">
                    <label class="block text-sm font-medium text-gray-700 mb-2">播报语言</label>
                    <div class="flex space-x-3">
                        <button class="language-btn px-3 py-1.5 bg-primary/10 text-primary rounded-lg text-sm font-medium btn-hover" data-lang="zh">
                            中文
                        </button>
                        <button class="language-btn px-3 py-1.5 bg-gray-200 text-gray-700 rounded-lg text-sm font-medium btn-hover" data-lang="en">
                            英文
                        </button>
                    </div>
                </div>
            </div>
        </div>
        
        <!-- 状态显示区域 -->
        <div id="statusDisplay" class="mb-4 text-center py-2 px-4 rounded-lg bg-gray-100 text-gray-600 text-sm">
            准备就绪
        </div>
    </div>
    
    <script>
        // 全局变量
        let timerInterval = null;
        let startTime = 0;
        let accumulatedTime = 0;
        let isRunning = false;
        let currentPhase = 0;
        let soundContext = null;
        let currentLang = 'zh';
        let scheduledEvents = [];
        let lastSecond = -1; // 记录上一秒数，用于精确播报
        
        // DOM元素
        const timerDisplay = document.getElementById('timerDisplay');
        const startBtn = document.getElementById('startBtn');
        const stopBtn = document.getElementById('stopBtn');
        const varAInput = document.getElementById('varA');
        const varBInput = document.getElementById('varB');
        const varTInput = document.getElementById('varT');
        const statusDisplay = document.getElementById('statusDisplay');
        const beepSoundRadio = document.getElementById('beepSound');
        const speechSoundRadio = document.getElementById('speechSound'); // 修改为语音播报
        const languageSelector = document.getElementById('languageSelector');
        const languageBtns = document.querySelectorAll('.language-btn');
        
        // 初始化
        document.addEventListener('DOMContentLoaded', () => {
            // 初始化音频上下文
            if (window.AudioContext || window.webkitAudioContext) {
                soundContext = new (window.AudioContext || window.webkitAudioContext)();
            } else {
                alert('您的浏览器不支持Web Audio API，蜂鸣音功能将无法使用。');
            }
            
            // 事件监听
            startBtn.addEventListener('click', startSequence);
            stopBtn.addEventListener('click', stopSequence);
            beepSoundRadio.addEventListener('change', toggleSoundOptions);
            speechSoundRadio.addEventListener('change', toggleSoundOptions); // 修改为语音播报
            languageBtns.forEach(btn => {
                btn.addEventListener('click', setLanguage);
            });
            
            // 验证输入
            [varAInput, varBInput, varTInput].forEach(input => {
                input.addEventListener('input', validateInputs);
            });
            validateInputs();
        });
        
        // 输入验证
        function validateInputs() {
            const a = parseFloat(varAInput.value);
            const b = parseFloat(varBInput.value);
            
            if (isNaN(a) || isNaN(b) || a <= 0 || b <= 0 || a >= b) {
                startBtn.disabled = true;
                startBtn.classList.add('opacity-50', 'cursor-not-allowed');
                statusDisplay.textContent = '错误：变量A必须小于变量B，且均为正数';
                statusDisplay.classList.remove('bg-gray-100', 'bg-green-100', 'bg-yellow-100');
                statusDisplay.classList.add('bg-red-100', 'text-red-700');
            } else {
                startBtn.disabled = false;
                startBtn.classList.remove('opacity-50', 'cursor-not-allowed');
                if (!isRunning) {
                    statusDisplay.textContent = '准备就绪';
                    statusDisplay.classList.remove('bg-red-100', 'bg-green-100', 'bg-yellow-100');
                    statusDisplay.classList.add('bg-gray-100', 'text-gray-600');
                }
            }
        }
        
        // 切换音效选项显示
        function toggleSoundOptions() {
            if (speechSoundRadio.checked) { // 修改为语音播报
                languageSelector.classList.remove('hidden');
            } else {
                languageSelector.classList.add('hidden');
            }
        }
        
        // 设置语言
        function setLanguage(e) {
            currentLang = e.target.dataset.lang;
            
            // 更新按钮样式
            languageBtns.forEach(btn => {
                if (btn.dataset.lang === currentLang) {
                    btn.classList.remove('bg-gray-200', 'text-gray-700');
                    btn.classList.add('bg-primary/10', 'text-primary');
                } else {
                    btn.classList.remove('bg-primary/10', 'text-primary');
                    btn.classList.add('bg-gray-200', 'text-gray-700');
                }
            });
        }
        
        // 开始整个发令序列
        function startSequence() {
            if (isRunning) return;
            // 首次点击时强制初始化语音引擎（移动设备不发声的关键修改）
            if ('speechSynthesis' in window && !window.speechInitialized) {
                    const test = new SpeechSynthesisUtterance('初始化语音');
                    test.volume = 0; // 不发出声音
                    window.speechSynthesis.speak(test);
                    setTimeout(() => window.speechSynthesis.cancel(), 100);
                    window.speechInitialized = true;
                }           
            isRunning = true;
            startBtn.disabled = true;
            startBtn.classList.add('opacity-50', 'cursor-not-allowed');
            stopBtn.disabled = false;
            stopBtn.classList.remove('opacity-50', 'cursor-not-allowed');
            
            // 重置计时器
            accumulatedTime = 0;
            updateTimerDisplay();
            lastSecond = -1; // 重置上一秒数
            
            // 获取参数
            // const a = parseFloat(varAInput.value);
            // const b = parseFloat(varBInput.value);
            // const t = varTInput.value ? parseFloat(varTInput.value) : null;
            const readyToSet = parseFloat(document.getElementById('readyToSet').value);
            const setToGoA = parseFloat(document.getElementById('setToGoA').value);
            const setToGoB = parseFloat(document.getElementById('setToGoB').value);
            const countdownT = document.getElementById('countdownT').value 
                     ? parseFloat(document.getElementById('countdownT').value) 
                     : null;
            // 清除之前的计划事件
            clearScheduledEvents();
            
            // 准备阶段
            currentPhase = 0;
            statusDisplay.textContent = '准备开始...';
            statusDisplay.classList.remove('bg-gray-100', 'bg-red-100', 'bg-green-100');
            statusDisplay.classList.add('bg-yellow-100', 'text-yellow-700');
            
            // 计划各就位
            const readyTime = 1;
            scheduleEvent(playReady, readyTime);
            
            // 计划预备
            const setTime = readyTime + readyToSet;
            scheduleEvent(playSet, setTime);
            
            // 计划发令枪响
            const goTime = setTime + getRandomTime(setToGoA, setToGoB);
            scheduleEvent(startTimerAndPlayGo, goTime);
            
            // 如果设置了countdownT，计划到时提示
            if (countdownT) {
                scheduleEvent(playTimeUp, goTime + countdownT);
            }
        }
        
        // 停止整个序列
        function stopSequence() {
            if (!isRunning) return;
            
            isRunning = false;
            startBtn.disabled = false;
            startBtn.classList.remove('opacity-50', 'cursor-not-allowed');
            stopBtn.disabled = true;
            stopBtn.classList.add('opacity-50', 'cursor-not-allowed');
            
            // 清除计时器
            if (timerInterval) {
                clearInterval(timerInterval);
                timerInterval = null;
            }
            
            // 清除计划事件
            clearScheduledEvents();
            
            // 重置状态
            currentPhase = 0;
            document.body.classList.remove('bg-pulse');
            statusDisplay.textContent = '已停止';
            statusDisplay.classList.remove('bg-yellow-100', 'bg-red-100', 'bg-green-100');
            statusDisplay.classList.add('bg-gray-100', 'text-gray-600');
            
            // 重置计时器显示
            accumulatedTime = 0;
            updateTimerDisplay();
            lastSecond = -1; // 重置上一秒数
            
            // 关闭音频上下文
            if (soundContext && soundContext.state !== 'closed') {
                soundContext.suspend();
            }
        }
        
        // 安排事件
        function scheduleEvent(callback, delay) {
            const timeoutId = setTimeout(() => {
                callback();
                // 从计划事件列表中移除
                scheduledEvents = scheduledEvents.filter(id => id !== timeoutId);
            }, delay * 1000);
            
            scheduledEvents.push(timeoutId);
        }
        
        // 清除所有计划的事件
        function clearScheduledEvents() {
            scheduledEvents.forEach(id => clearTimeout(id));
            scheduledEvents = [];
        }
        
        // 播放"各就位"
        function playReady() {
            currentPhase = 1;
            statusDisplay.textContent = '各就位';
            playSound(1);
        }
        
        // 播放"预备"
        function playSet() {
            currentPhase = 2;
            statusDisplay.textContent = '预备';
            playSound(2);
        }
        
        // 开始计时器并播放"砰"
        function startTimerAndPlayGo() {
            currentPhase = 3;
            statusDisplay.textContent = '计时中...';
            statusDisplay.classList.remove('bg-yellow-100');
            statusDisplay.classList.add('bg-green-100', 'text-green-700');
            
            // 播放发令枪声
            playSound(3);
            
            // 页面闪烁效果
            document.body.classList.add('bg-pulse');
            setTimeout(() => {
                document.body.classList.remove('bg-pulse');
            }, 50);
            
            // 开始计时
            startTime = Date.now();
            updateTimerDisplay();
            
            timerInterval = setInterval(() => {
                updateTimerDisplay();
                
                // 精确检测秒数变化
                const totalSeconds = Math.floor(accumulatedTime / 1000);
                if (totalSeconds > lastSecond) {
                    lastSecond = totalSeconds;
                    
                    if (totalSeconds > 0) {
                        playBeep(600); // 报时声频率600Hz
                        
                        // 如果是语音播报模式，播报秒数
                        if (speechSoundRadio.checked && totalSeconds <= 60) {
                            speakNumber(totalSeconds);
                        }
                    }
                }
            }, 10);
        }
        
        // 播放到时提示音
        function playTimeUp() {
            if (!isRunning) return;
            
            statusDisplay.textContent = '时间到！';
            statusDisplay.classList.remove('bg-green-100');
            statusDisplay.classList.add('bg-red-100', 'text-red-700');
            
            // 播放长蜂鸣
            if (beepSoundRadio.checked) {
                playBeep(800, 1000); // 800Hz，持续1秒
            } else {
                // 语音播报模式也播放蜂鸣
                playBeep(800, 1000);
            }
            
            // 停止计时器
            if (timerInterval) {
                clearInterval(timerInterval);
                timerInterval = null;
            }
        }
        
        // 播放相应的声音
        function playSound(type) {
            if (beepSoundRadio.checked) {
                // 蜂鸣音
                let frequency;
                switch(type) {
                    case 1: frequency = 200; break; // 各就位
                    case 2: frequency = 400; break; // 预备
                    case 3: frequency = 800; break; // 砰
                    default: frequency = 440;
                }
                playBeep(frequency);
            } else {
                // 语音播报
                let text;
                if (currentLang === 'zh') {
                    switch(type) {
                        case 1: text = '各就位'; break;
                        case 2: text = '预备'; break;
                        case 3: // 发令枪播放蜂鸣
                            playBeep(800,500,'square');
                            return;
                        default: text = '开始';
                    }
                } else {
                    switch(type) {
                        case 1: text = 'On your marks'; break;
                        case 2: text = 'Set'; break;
                        case 3: // 发令枪播放蜂鸣
                            playBeep(800,500,'square');
                            return;
                        default: text = 'Go';
                    }
                }
                // setTimeout(() => {
                //     speakText(text);
                // }, 10)
                speakText(text);
            }
        }
       

        // 播放蜂鸣音
        function playBeep(frequency, duration = 300, oscillatorType='sine') {
            if (!soundContext || soundContext.state === 'closed') {
                soundContext = new (window.AudioContext || window.webkitAudioContext)();
            } else if (soundContext.state === 'suspended') {
                soundContext.resume();
            }
            
            const oscillator = soundContext.createOscillator();
            const gainNode = soundContext.createGain();
            
            oscillator.type = oscillatorType;
            oscillator.frequency.setValueAtTime(frequency, soundContext.currentTime);
            
            gainNode.gain.setValueAtTime(0, soundContext.currentTime);
            gainNode.gain.linearRampToValueAtTime(0.5, soundContext.currentTime + 0.05);
            gainNode.gain.linearRampToValueAtTime(0, soundContext.currentTime + duration / 1000);
            
            oscillator.connect(gainNode);
            gainNode.connect(soundContext.destination);
            
            oscillator.start();
            oscillator.stop(soundContext.currentTime + duration / 1000);
        }
        
        // 语音播报文本
        function speakText(text,rate=1) {
            if ('speechSynthesis' in window) {
                // 停止正在进行的语音
                window.speechSynthesis.cancel();
                
                const utterance = new SpeechSynthesisUtterance(text);
                utterance.lang = currentLang === 'zh' ? 'zh-CN' : 'en-US';
                utterance.rate = rate;
                utterance.volume = 1;
                // 添加错误处理
                utterance.onerror = (e) => console.error('语音错误:', e);
                
                window.speechSynthesis.speak(utterance);
            }
        }
        // 优化英文数字转换函数
        function numberToEnglish(number) {
            // 创建静态映射表
            const numberWords = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 
                                'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty'];
            const tensWords = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];
            
            if (number <= 20) {
                return numberWords[number];
            } else if (number < 100) {
                const tens = Math.floor(number / 10);
                const ones = number % 10;
                return ones ? `${tensWords[tens]} ${numberWords[ones]}` : tensWords[tens];
            } else {
                return number.toString(); // 处理大于60的情况
            }
        }
        // 语音播报数字
        function speakNumber(number) {
            if ('speechSynthesis' in window) {
                let text;
                if (currentLang === 'zh') {
                    text = number.toString();
                } else {
                    text = numberToEnglish(number); // 使用优化后的转换函数
                }
                
                speakText(text,1.5);
            }
        }
        
        // 更新计时器显示
        function updateTimerDisplay() {
            if (isRunning && currentPhase === 3) {
                accumulatedTime = Date.now() - startTime;
            }
            
            const totalMilliseconds = accumulatedTime;
            const milliseconds = Math.floor(totalMilliseconds % 1000);
            const totalSeconds = Math.floor(totalMilliseconds / 1000);
            const seconds = totalSeconds % 60;
            const minutes = Math.floor(totalSeconds / 60);
            
            const displayMinutes = String(minutes).padStart(2, '0');
            const displaySeconds = String(seconds).padStart(2, '0');
            const displayMilliseconds = String(milliseconds).padStart(3, '0').substring(0, 3);
            
            timerDisplay.textContent = `${displayMinutes}:${displaySeconds}:${displayMilliseconds}`;
        }
        
        // 获取a和b之间的随机数
        function getRandomTime(a, b) {
            return a + Math.random() * (b - a);
        }
    </script>
</body>
</html>
    